package com.eversec.util.ftp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;

import com.eversec.util.command.FileOper;
import com.eversec.util.command.TimeUtil;


public class FtpUpload {
private static Logger log = Logger.getLogger(FtpUpload.class);
	
	private FTPClient ftpClient = new FTPClient();
	
	private static Integer port = Integer.valueOf(21);
	private static String path = "/";
	
	/**
	 *  ftp上传
	 */
	public static boolean ftpUpload(String filePath, String ftpPath, String ftpIP, String ftpUserName, String ftpPwd){
		log.info(">>>>>>>>>>>>>>>>>>>>>ftp开始上传");
		FtpUpload myFtp = new FtpUpload();  
	    try {   
	        myFtp.connect(ftpIP, ftpUserName, ftpPwd); 
	        FTPClient ftpclient = myFtp.ftpClient;
	        ftpclient.enterLocalPassiveMode();
	        String fileNameBegin = new File(filePath).getName();
	        String fileNameEnd = FileOper.getFileNameNoStuffix(filePath) + ".xml";
//	        String filepath = path + ftpPath + "/" + TimeUtil.getDateString() + "/" + TimeUtil.getTime() + "/" + fileNameBegin;
	        String filepath = path + ftpPath + "/" + TimeUtil.getDateString() + "/" + fileNameBegin;
	        log.info(">>>>>>>>>>>>>>>>>>>>>>>上传的路径为:" + filepath);
	        myFtp.upload(filePath, filepath);
	        ftpclient.rename(fileNameBegin, fileNameEnd);
	        myFtp.disconnect();
	        log.info(">>>>>>>>>>>>>>>>>>>>>>>文件上传成功！");
	        return true;
	    }catch (IOException e) {   
	    	log.error("连接FTP出错："+e.getMessage());   
	    	e.printStackTrace();
	    	return false;
        }
	}
	
	/** 
	  * 连接到FTP服务器 
	  * @param hostname 主机名 
	  * @param port 端口 
	  * @param username 用户名 
	  * @param password 密码 
	  * @return 是否连接成功 
	  * @throws IOException 
	  */ 
	 public boolean connect(String ip, String userName, String password) throws IOException{  
	     ftpClient.connect(ip, port);   
	     ftpClient.setControlEncoding("GBK");   
	     if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode())){   
	         if(ftpClient.login(userName, password)){   
	             return true;   
	         }   
	     }   
	     ftpClient.disconnect();   
	     return false;   
	 }
	 
	 /** 
	  * 从FTP服务器上下载文件,支持断点续传，上传百分比汇报 
	  * @param remote 远程文件路径 
	  * @param local 本地文件路径 
	  * @return 上传的状态 
	  * @throws IOException 
	  */ 
	 public DownloadStatus download(String remote,String local) throws IOException{   
	     //设置被动模式   
	     ftpClient.enterLocalPassiveMode();   
	     //设置以二进制方式传输   
	     ftpClient.setFileType(FTP.BINARY_FILE_TYPE);   
	     DownloadStatus result;   
	        
	     //检查远程文件是否存在   
	     FTPFile[] files = ftpClient.listFiles(new String(remote.getBytes("GBK"),"iso-8859-1"));  
	     if(files.length != 1){   
	         log.error("远程文件不存在");   
	         return DownloadStatus.Remote_File_Noexist;   
	     }   
	        
	     long lRemoteSize = files[0].getSize();   
	     File f = new File(local);   
	     //本地存在文件，进行断点下载   
	     if(f.exists()){   
	         long localSize = f.length();   
	         //判断本地文件大小是否大于远程文件大小   
	         if(localSize >= lRemoteSize){   
	             log.error("文件已存在，下载中止");   
	             return DownloadStatus.Local_Bigger_Remote;   
	         }   
	            
	         //进行断点续传，并记录状态   
	         FileOutputStream out = new FileOutputStream(f,true);   
	         ftpClient.setRestartOffset(localSize);   
	         InputStream in =  ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1"));   
	         byte[] bytes = new byte[1024];   
	         long step = lRemoteSize / 100;   
	         long process=localSize / step;   
	         int c;   
	         while((c = in.read(bytes))!= -1){   
	             out.write(bytes,0,c);   
	             localSize+=c;   
	             long nowProcess = localSize / step;   
	             if(nowProcess > process){   
	            	 if(process <= 100){
//	                	 System.out.println("下载进度："+ process + "%");
	                 }else{
//	                	 System.out.println("下载进度：100%");
	                 }     
	             }   
	         }   
	         in.close();   
	         out.close();   
	         boolean isDo = ftpClient.completePendingCommand();   
	         if(isDo){   
	             result = DownloadStatus.Download_From_Break_Success;  
	             log.info("断点文件下载成功！");
	         }else {   
	             result = DownloadStatus.Download_From_Break_Failed;   
	             log.info("断点文件下载失败！");
	         }   
	     }else {   
	         OutputStream out = new FileOutputStream(f);   
	         InputStream in=  ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1"));   
	         byte[] bytes = new byte[1024];   
	         long step = lRemoteSize / 100 + 1;   
	         long process=0;   
	         long localSize = 0L;   
	         int c;   
	         while((c = in.read(bytes))!= -1){   
	             out.write(bytes, 0, c);   
	             localSize+=c;   
	             long nowProcess = localSize / step;   
	             if(nowProcess > process){   
	                 process = nowProcess;   
	                 if(process <= 100){
//	                	 System.out.println("下载进度："+ process + "%");
	                 }else{
//	                	 System.out.println("下载进度：100%");
	                 }   
	             }   
	         }   
	         in.close();   
	         out.close();   
	         boolean upNewStatus = ftpClient.completePendingCommand();   
	         if(upNewStatus){   
	             result = DownloadStatus.Download_New_Success;
	             log.info("文件下载成功！");
	         }else {   
	             result = DownloadStatus.Download_New_Failed;  
	             log.info("文件下载失败！");
	         }   
	     }   
	     return result;   
	 }   
	    
	 /** 
	  * 上传文件到FTP服务器，支持断点续传 
	  * @param local 本地文件名称，绝对路径 
	  * @param remote 远程文件路径，使用/home/directory1/subdirectory/file.ext 按照Linux上的路径指定方式，支持多级目录嵌套，支持递归创建不存在的目录结构 
	  * @return 上传结果 
	  * @throws IOException 
	  */ 
	 public UploadStatus upload(String local,String remote) throws IOException{   
	     //设置PassiveMode传输   
	     ftpClient.enterLocalPassiveMode();   
	     //设置以二进制流的方式传输   
	     ftpClient.setFileType(FTP.BINARY_FILE_TYPE);   
	     ftpClient.setControlEncoding("GBK");   
	     UploadStatus result;   
	     //对远程目录的处理   
	     String remoteFileName = remote;   
	     if(remote.contains("/")){   
	         remoteFileName = remote.substring(remote.lastIndexOf("/")+1);   
	         //创建服务器远程目录结构，创建失败直接返回   
	         if(createDirecroty(remote, ftpClient)==UploadStatus.Create_Directory_Fail){   
	             return UploadStatus.Create_Directory_Fail;   
	         }   
	     }   
	        
	     //检查远程是否存在文件   
	     FTPFile[] files = ftpClient.listFiles(new String(remoteFileName.getBytes("GBK"),"iso-8859-1"));   
	     if(files.length == 1){   
	         long remoteSize = files[0].getSize();   
	         File f = new File(local);   
	         long localSize = f.length();   
	         if(remoteSize==localSize){   //文件存在
	             return UploadStatus.File_Exits;   
	         }else if(remoteSize > localSize){   
	             return UploadStatus.Remote_Bigger_Local;   
	         }   
	            
	         //尝试移动文件内读取指针,实现断点续传   
	         result = uploadFile(remoteFileName, f, ftpClient, remoteSize);   
	            
	         //如果断点续传没有成功，则删除服务器上文件，重新上传   
	         if(result == UploadStatus.Upload_From_Break_Failed){   
	             if(!ftpClient.deleteFile(remoteFileName)){   
	                 return UploadStatus.Delete_Remote_Faild;   
	             }   
	             result = uploadFile(remoteFileName, f, ftpClient, 0);   
	         }   
	     }else {   
	         result = uploadFile(remoteFileName, new File(local), ftpClient, 0);   
	     }   
	     return result;   
	 }   
	 /** 
	  * 断开与远程服务器的连接 
	  * @throws IOException 
	  */ 
	 public void disconnect() throws IOException{   
	     if(ftpClient.isConnected()){   
	         ftpClient.disconnect();   
	     }   
	 }   
	    
	 /** 
	  * 递归创建远程服务器目录 
	  * @param remote 远程服务器文件绝对路径 
	  * @param ftpClient FTPClient对象 
	  * @return 目录创建是否成功 
	  * @throws IOException 
	  */ 
	 public UploadStatus createDirecroty(String remote,FTPClient ftpClient) throws IOException{   
	     UploadStatus status = UploadStatus.Create_Directory_Success;   
	     String directory = remote.substring(0,remote.lastIndexOf("/")+1);   
	     if(!directory.equalsIgnoreCase("/")&&!ftpClient.changeWorkingDirectory(new String(directory.getBytes("GBK"),"iso-8859-1"))){   
	         //如果远程目录不存在，则递归创建远程服务器目录   
	         int start=0;   
	         int end = 0;   
	         if(directory.startsWith("/")){   
	             start = 1;   
	         }else{   
	             start = 0;   
	         }   
	         end = directory.indexOf("/",start);   
	         while(true){   
	             String subDirectory = new String(remote.substring(start,end).getBytes("GBK"),"iso-8859-1");
	             boolean flag = ftpClient.changeWorkingDirectory(subDirectory);
	             if(!flag){ 
	            	 boolean flag2 = ftpClient.makeDirectory(subDirectory);
	                 if(flag2){   
	                     ftpClient.changeWorkingDirectory(subDirectory);   
	                 }else {   
	                     log.error(">>>>>>>>>>>>>>>>>>>>>>>创建目录失败");   
	                     return UploadStatus.Create_Directory_Fail;   
	                 }   
	             }   
	                
	             start = end + 1;   
	             end = directory.indexOf("/",start);   
	                
	             //检查所有目录是否创建完毕   
	             if(end <= start){   
	                 break;   
	             }   
	         }   
	     }   
	     return status;   
	 }   
	    
	 /** 
	  * 上传文件到服务器,新上传和断点续传 
	  * @param remoteFile 远程文件名，在上传之前已经将服务器工作目录做了改变 
	  * @param localFile 本地文件File句柄，绝对路径 
	  * @param processStep 需要显示的处理进度步进值 
	  * @param ftpClient FTPClient引用 
	  * @return 
	  * @throws IOException 
	  */ 
	 public UploadStatus uploadFile(String remoteFile,File localFile,FTPClient ftpClient,long remoteSize) throws IOException{   
	     UploadStatus status;   
	     //显示进度的上传   
	     long step = localFile.length() / 100;   
	     long process = 0;   
	     long localreadbytes = 0L;   
	     RandomAccessFile raf = new RandomAccessFile(localFile,"r");   
	     OutputStream out = ftpClient.appendFileStream(new String(remoteFile.getBytes("GBK"),"iso-8859-1"));   
	     //断点续传   
	     if(remoteSize>0){   
	         ftpClient.setRestartOffset(remoteSize);   
	         process = remoteSize /step;   
	         raf.seek(remoteSize);   
	         localreadbytes = remoteSize;   
	     }   
	     byte[] bytes = new byte[1024];   
	     int c;   
	     while((c = raf.read(bytes))!= -1){   
	         out.write(bytes,0,c);   
	         localreadbytes+=c;   
	         if(localreadbytes / step != process){   
	             process = localreadbytes / step;   
//	             System.out.println("上传进度:" + process);    
	         }   
	     }   
	     out.flush();   
	     raf.close();   
	     out.close();   
	     boolean result =ftpClient.completePendingCommand();   
	     if(remoteSize > 0){   
	         status = result?UploadStatus.Upload_From_Break_Success:UploadStatus.Upload_From_Break_Failed;   
	     }else {   
	         status = result?UploadStatus.Upload_New_File_Success:UploadStatus.Upload_New_File_Failed;   
	     }   
	     return status;   
	 }   
	 
	 public boolean isMatch(String name){
		 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		 String today = sdf.format(new Date());
		 return name.indexOf(today)==-1 ? false : true;
	 }
	 
	/**
	 * 将时间戳转为字符串 
	 * @param timestamp
	 * @return 
	 */
	public static String getYesterday() { 
		String time = null; 
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 
		time = sdf.format(new Date().getTime()-24*60*60*1000); 
		return time; 
	} 
	
    public enum UploadStatus {   
        Create_Directory_Fail,      //远程服务器相应目录创建失败   
	    Create_Directory_Success,   //远程服务器闯将目录成功   
	    Upload_New_File_Success,    //上传新文件成功   
	    Upload_New_File_Failed,     //上传新文件失败   
	    File_Exits,                 //文件已经存在   
	    Remote_Bigger_Local,        //远程文件大于本地文件   
	    Upload_From_Break_Success, //断点续传成功   
	    Upload_From_Break_Failed,   //断点续传失败   
	    Delete_Remote_Faild;        //删除远程文件失败   
    }
	
    public enum DownloadStatus {   
		Remote_File_Noexist, //远程文件不存在   
	    Download_New_Success,    //下载文件成功   
	    Download_New_Failed,     //下载文件失败    
	    Local_Bigger_Remote,        //本地文件大于远程文件
	    Download_From_Break_Success, //断点续传成功   
	    Download_From_Break_Failed;   //断点续传失败   
    } 
}
